package org.yixiu.activiti.util;

import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.yixiu.activiti.entry.Person;

import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipInputStream;

@Slf4j
public class WorkflowUtil {

    private static ProcessEngine processEngine = null;

    static {
        processEngine = ProcessEngines.getDefaultProcessEngine();
    }

    /**
     * 部署工作流
     * 在 {@link DeploymentBuilder} 中定义了5种部署方式
     * @param t  流程定义文件信息
     * @param deployName 流程部署名称
     * @param <T> 流程定义文件信息类型
     */
    public static <T> void deploy(T t,String deployName){
        //addInputStream 根据流进行部署
        if(t instanceof InputStream){
            InputStream stream = (InputStream)t;
            processEngine.getRepositoryService()
                    .createDeployment()
                    .name(deployName)
                    .addInputStream("ProcessInputStream",stream)
                    .deploy();
        }else if(t instanceof String){
            String file = (String)t;
            //addClasspathResource 根据resource部署。
            if(file.endsWith(".bpmn")){
                processEngine.getRepositoryService()
                        .createDeployment()
                        .name(deployName)
                        .addClasspathResource(file)
                        .deploy();
            }
            //addZipInputStream 根据zip流进行部署。
            else if(file.endsWith("zip")){
                InputStream inputStream = WorkflowUtil.class.getClassLoader().getResourceAsStream(file);
                ZipInputStream zipInputStream = new ZipInputStream(inputStream);
                processEngine.getRepositoryService()
                        .createDeployment()
                        .name(deployName)
                        .addZipInputStream(zipInputStream)
                        .deploy();
            }else{
                //将文件内容拼接成字符串进行部署
                processEngine.getRepositoryService()
                        .createDeployment()
                        .name(deployName)
                        .addString("ProcessFileStr",file)
                        .deploy();
            }
        }
        //根据BpmnModel进行部署。使用场景就是自己设计一个流程设计器画布，去解析成bpmn规范文件。适合动态的拓展。
        else if(t instanceof BpmnModel){
            BpmnModel model = (BpmnModel) t;
            processEngine.getRepositoryService()
                    .createDeployment()
                    .name(deployName)
                    .addBpmnModel("ProcessBpmnModel",model)
                    .deploy();
        }else{
            throw new ActivitiIllegalArgumentException("argument for deployment is wrong type");
        }
    }

    /**
     * 启动流程 通过流程的key值启动，默认按最新版本启动
     * @param processKey 流程key值 例：purchase
     */
    public static void startProcessByKey(String processKey){
        ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByKey(processKey);
        log.info("流程启动成功! 流程ID:{},Activiti ID:{}",processInstance.getId(),processInstance.getActivityId());
    }

    /**
     * 启动流程 通过流程的ID启动
     * @param processId 例：purchase:2:5009
     */
    public static void startProcessById(String processId){
        ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceById(processId);
        log.info("流程启动成功! 流程ID:{},Activiti ID:{}",processInstance.getId(),processInstance.getActivityId());
    }

    /**
     * 查询对应处理人的任务列表
     * @param assignee 处理人
     * @return
     */
    public static List<Task> queryTaskList(String assignee){
        List<Task> taskList = processEngine.getTaskService().createTaskQuery().taskAssignee(assignee).list();
        taskList.forEach(task -> {
            log.info("任务Id:{}，任务名称：{}",task.getId(),task.getName());
        });
        return taskList;
    }

    /**
     * 完成任务
     * @param taskId 任务ID
     */
    public static void completeTask(String taskId){
        processEngine.getTaskService().complete(taskId);
        log.info("任务({})完成!",taskId);
    }

    /**
     * 查询流程定义
     * @return
     */
    public static List<ProcessDefinition> queryProcessDefinition(){
        List<ProcessDefinition> processDefinitionList = processEngine.getRepositoryService().createProcessDefinitionQuery()
                //按版本排序
                .orderByProcessDefinitionVersion().asc()
                .list();

        /*processEngine.getRepositoryService().createProcessDefinitionQuery()
                //添加查询条件
                .processDefinitionId("id")
                .processDefinitionName("name")
                .processDefinitionKey("key")
                //计数
                //.count();
                //分页查询
                //.listPage(firstResult,maxResults);
                //唯一结果集
                .singleResult();*/

        processDefinitionList.forEach(processDefinition -> {
            log.info("流程ID:{},流程名称:{},流程key:{},流程版本:{},流程资源名称:{}",
                    processDefinition.getId(),processDefinition.getName(),processDefinition.getKey(),
                    processDefinition.getVersion(),processDefinition.getResourceName()
            );
        });

        return processDefinitionList;
    }

    /**
     * 查询最新版本的流程定义
     * @return
     */
    public static List<ProcessDefinition> queryLatestProcessDefinitions(){
        List<ProcessDefinition> processDefinitions = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion().asc()
                .list();

        Map<String,ProcessDefinition> processDefinitionMap = new LinkedHashMap<>();
        //processDefinitions按版本升序，最新的版本会覆盖旧的版本
        processDefinitions.forEach( processDefinition ->
                processDefinitionMap.put(processDefinition.getKey(),processDefinition));

        List<ProcessDefinition> definitions = new LinkedList<>();
        processDefinitionMap.forEach( (s, processDefinition) -> {
            definitions.add(processDefinition);
            log.info("流程ID:{},流程名称:{},流程key:{},流程版本:{},流程资源名称:{}",
                    processDefinition.getId(),processDefinition.getName(),processDefinition.getKey(),
                    processDefinition.getVersion(),processDefinition.getResourceName());
        });

        return definitions;
    }

    /**
     * 删除流程定义 --- 根据key值删除，删除所有key值相同的不同版本的流程定义
     * @param processKey 流程key值
     */
    public static void deleteProcessDefinitionByKey(String processKey){
        List<ProcessDefinition> processDefinitions = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .processDefinitionKey(processKey)
                .list();

        processDefinitions.forEach( processDefinition -> {
            //true 表示 级联删除
            processEngine.getRepositoryService().deleteDeployment(processDefinition.getDeploymentId(),true);
            log.info("流程({}---{})删除成功！",processDefinition.getDeploymentId(),processDefinition.getName());
        });
    }

    /**
     * 查询流程执行状态
     * @param processInstanceId  流程ID
     * @return 返回流程正在执行的Activiti 的 ID
     */
    public static String queryProcessState(String processInstanceId){
        ProcessInstance processInstance = processEngine.getRuntimeService()
                .createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        if(null != processInstance){
            log.info("当前流程{}({})执行到{}",processInstance.getProcessDefinitionId(),processInstance.getId(),processInstance.getActivityId());
            return processInstance.getActivityId();
        }else{
            log.info("当前流程{}({})已结束！",processInstance.getProcessDefinitionId(),processInstance.getId());
            return "";
        }
    }

    /**
     * 查询处理人历史任务
     * @param assignee
     * @return
     */
    public static List<HistoricTaskInstance> queryHistoryTasks(String assignee){
        List<HistoricTaskInstance> historicTaskInstances = processEngine.getHistoryService()
                .createHistoricTaskInstanceQuery()
                .taskAssignee(assignee)
                .list();

        historicTaskInstances.forEach( historicTaskInstance -> {
            log.info("任务ID:{},流程实力ID:{},任务办理人:{},执行对象ID:{}",
                    historicTaskInstance.getId(),historicTaskInstance.getProcessInstanceId(),
                    historicTaskInstance.getAssignee(),historicTaskInstance.getExecutionId());
            log.info("任务执行---开始时间:{}--结束时间:{}--周期:{}",
                    historicTaskInstance.getStartTime(),historicTaskInstance.getEndTime(),
                    historicTaskInstance.getDurationInMillis());
        });

        return historicTaskInstances;
    }

    /**
     * 查询历史流程实例
     * @param processInstanceId
     * @return
     */
    public static HistoricProcessInstance queryHistoryProcessInstance(String processInstanceId){
        HistoricProcessInstance historicProcessInstance = processEngine.getHistoryService()
                .createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        log.info("流程定义ID:{},流程实例ID:{},流程开始时间:{},流程结束时间:{},流程执行时间:{}",
                historicProcessInstance.getProcessDefinitionId(),historicProcessInstance.getId(),
                historicProcessInstance.getStartTime(),historicProcessInstance.getEndTime(),
                historicProcessInstance.getDurationInMillis());
        return historicProcessInstance;
    }

    /**
     * 查询按照某个流程定义的规则一共执行了多少次流程
     * @param processDefinitionKey 流程定义Key值
     * @return
     */
    public static List<HistoricProcessInstance> queryHistoryProcessInstances(String processDefinitionKey){
        List<HistoricProcessInstance> instances = processEngine.getHistoryService()
                .createHistoricProcessInstanceQuery()
                .processDefinitionKey(processDefinitionKey)
                .orderByProcessInstanceStartTime().desc()
                .list();

        instances.forEach(historicProcessInstance -> {
            log.info("流程定义ID:{},流程实例ID:{},流程开始时间:{},流程结束时间:{},流程执行时间:{}",
                    historicProcessInstance.getProcessDefinitionId(),historicProcessInstance.getId(),
                    historicProcessInstance.getStartTime(),historicProcessInstance.getEndTime(),
                    historicProcessInstance.getDurationInMillis());
        });

        return instances;
    }

    /**
     * 查询某一次流程的执行一共经历了多少活动
     * @param processInstanceId 流程实例ID
     * @return
     */
    public static List<HistoricActivityInstance> queryHistoryActivityInstance(String processInstanceId){
        List<HistoricActivityInstance> instances = processEngine.getHistoryService()
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceEndTime().asc()
                .list();

        instances.forEach(instance -> {
            log.info("活动ID:{},活动名称:{},活动类型:{},活动所属流程实例ID:{},活动执行者:{}," +
                    "活动开始时间:{},活动结束时间:{},活动执行时间:{}",
                    instance.getActivityId(),instance.getActivityName(),instance.getActivityType(),
                    instance.getProcessInstanceId(),instance.getAssignee(),
                    instance.getStartTime(),instance.getEndTime(),instance.getDurationInMillis());
        });

        return instances;
    }

    /**
     * 查询某一次流程的执行一共经历了多少任务
     * @param processInstanceId 流程实例ID
     * @return
     */
    public static List<HistoricTaskInstance> queryHistoryTaskInstance(String processInstanceId){
        List<HistoricTaskInstance> instances = processEngine.getHistoryService()
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricTaskInstanceEndTime().asc()
                .list();

        instances.forEach(instance -> {
            log.info("任务ID:{},任务名称:{},任务所属流程实例ID:{},任务执行者:{}," +
                            "活动开始时间:{},活动结束时间:{},活动执行时间:{}",
                    instance.getId(),instance.getName(),
                    instance.getProcessInstanceId(),instance.getAssignee(),
                    instance.getStartTime(),instance.getEndTime(),instance.getDurationInMillis());
        });

        return instances;
    }

    /**
     * 设置流程变量
     * @param processInstanceId 流程实例ID
     * @param assignee 任务办理人
     */
    public static void setVariables(String processInstanceId,String assignee){
        Task task = processEngine.getTaskService()
                .createTaskQuery()
                .processInstanceId(processInstanceId)
                .taskAssignee(assignee)
                .singleResult();

        processEngine.getTaskService().setVariable(task.getId(),"请假人","lisi");
        processEngine.getTaskService().setVariableLocal(task.getId(),"请假天数",5);
        processEngine.getTaskService().setVariable(task.getId(),"请假日期",new Date());

        Person person = new Person();
        person.setId("1");
        person.setName("wangwu");
        person.setAge(22);
        processEngine.getTaskService().setVariable(task.getId(),"人员信息",person);
    }

    /**
     * 查询流程变量
     * @param processInstanceId 流程实例ID
     * @param assignee 任务办理人
     */
    public static void getVariables(String processInstanceId,String assignee){
        Task task = processEngine.getTaskService()
                .createTaskQuery()
                .processInstanceId(processInstanceId)
                .taskAssignee(assignee)
                .singleResult();

        String name = (String)processEngine.getTaskService().getVariable(task.getId(), "请假人");
        Integer dates = (Integer)processEngine.getTaskService().getVariableLocal(task.getId(),"请假天数");
        Date date = (Date)processEngine.getTaskService().getVariable(task.getId(),"请假日期");
        Person person = (Person)processEngine.getTaskService().getVariable(task.getId(),"人员信息");

        log.info(name +"|"+ dates +"|"+ date +"|"+ person);
    }

    /**
     * 查询历史流程变量
     * @param varName 变量名称
     * @return
     */
    public static List<HistoricVariableInstance> queryHistoryVariables(String varName){
        List<HistoricVariableInstance> historicVariableInstances = processEngine.getHistoryService()
                .createHistoricVariableInstanceQuery()
                .variableName(varName)
                .list();

        historicVariableInstances.forEach(historicVariableInstance -> {
            log.info("VariableName:{},VariableValue:{}",historicVariableInstance.getVariableName(),historicVariableInstance.getValue());
        });

        return historicVariableInstances;
    }
}
